public class Operator {
    //基本四则运算符：加减乘除模(+ - * / %)
    public static void test1() {
        int a = 20;
        int b = 10;
        System.out.println(a + b); // 30
        System.out.println(a - b); // 10
        System.out.println(a * b); // 200
        System.out.println(a / b); // 2
        System.out.println(a % b); // 0 --->模运算相当于数学中除法的余数

        //注意：
        //上面的运算符都是二元运算符，使用时必须要有左右两个操作数
        //int / int 结果还是int类型，而且会向下取整

        int c = 3;
        int d = 2;
// 在数学中应该是1.5 但是在Java中输出结果为1 会向下取整，即小数点之后全部舍弃掉了
        System.out.println(c / d);
// 如果要得到数学中的结果，可以使用如下方式
//        double e = (double)c / d;
        double e = c * 1.0 / d;
        System.out.println(e);

        //做除法和取模时，右操作数不能为0
        int f = 1;
        int g = 0;
//        System.out.println(f / g);
//        System.out.println(f % g);

        //% 不仅可以对整型取模，也可以对double类型取模，但是意义不大，基本不使用,一般都是对整型取模的
        System.out.println(11.5 % 2);

        //两侧操作数类型不一致时，向类型大的提升
        System.out.println(1 + 0.2); // +的左侧是int，右侧是double，在加之前int被提升为double,所以输出1.2
    }

    //增量运算符 += -= *= %= /=
    public static void test2() {
        int a = 1;
        a += 2; // 相当于 a = a + 2
        System.out.println(a); // 输出3
        a -= 1; // 相当于 a = a - 1
        System.out.println(a); // 输出2
        a *= 3; // 相当于 a = a * 3
        System.out.println(a); // 输出6
        a /= 3; // 相当于 a = a / 3
        System.out.println(a); // 输出2
        a %= 3; // 相当于 a = a % 3
        System.out.println(a); // 输出2

        //注意：只有变量才能使用该运算符，常量不能使用
    }

    //自增/自减运算符++ --
    public static void test3() {
        int a = 1;
        a++; // 后置++ 表示给a的值加1，此时a的值为2
        System.out.println(a++); // 注意：后置++是先使用变量原来值，表示式结束时给变量+1，因此输出2
        System.out.println(a); // 输出3
        ++a; // 前置++ 表示给a的值加1
        System.out.println(++a); // 注意：前置++是先给变量+1，然后使用变量中的值，因此输出5
        System.out.println(a); // 输出5
        // --操作符给操作-1，与++含义类似
        //注意：
        //如果单独使用，【前置++】和【后置++】几乎没有区别
        //前置递增（++i）
        //前置递增运算符先增加变量的值，然后返回增加后的值。其操作步骤如下：
        //增加变量的值。
        //返回增加后的值。
        //后置递增（i++）
        //后置递增运算符先返回变量的当前值，然后再增加变量的值。其操作步骤如下：
        //保存变量的当前值。
        //增加变量的值。
        //返回保存的当前值。
        //后置递增需要多一步保存当前值的操作,所以后置++的效率会略低于前置++

        //如果混合使用，【前置++】先+1，然后使用变量+1之后的值，【后置++】先使用变量原来的值，表达式
        //结束时给变量+1
        //只有变量才能使用自增/自减运算符，常量不能使用，因为常量不允许被修改
    }

    //关系运算符
    //关系运算符主要有六个: == != < > <= >= ，其计算结果是true或者false
    public static void test4() {
        int a = 10;
        int b = 20;
        // 注意：在Java中 = 表示赋值，要与数学中的含义区分
        // 在Java中 == 表示相等
        System.out.println(a == b); // false
        System.out.println(a != b); // true
        System.out.println(a < b); // true
        System.out.println(a > b); // false
        System.out.println(a <= b); // true
        System.out.println(a >= b); // false

        //注意：当需要多次判断时，不能连着写，比如：3 < a < 5，Java程序与数学中是有区别的
    }

    //逻辑运算符(重点)
    //逻辑运算符主要有三个: && || ! ，运算结果都是 boolean类型
    public static void test5() {
        int a = 1;
        int b = 2;
        System.out.println(a == 1 && b == 2); // 左为真 且 右为真 则结果为真
        System.out.println(a == 1 && b > 100); // 左为真 但 右为假 则结果为假
        System.out.println(a > 100 && b == 2); // 左为假 但 右为真 则结果为假
        System.out.println(a > 100 && b > 100); // 左为假 且 右为假 则结果为假

        System.out.println(a == 1 || b == 2); // 左为真 且 右为真 则结果为真
        System.out.println(a == 1 || b > 100); // 左为真 但 右为假 则结果也为真
        System.out.println(a > 100 || b == 2); // 左为假 但 右为真 则结果也为真
        System.out.println(a > 100 || b > 100); // 左为假 且 右为假 则结果为假

        System.out.println(!(a == 1)); // a == 1 为true，取个非就是false
        System.out.println(!(a != 1)); // a != 1 为false，取个非就是true

        //短路求值
        //我们都知道, 计算 10 / 0 会导致程序抛出异常. 但是上面的代码却能正常运行, 说明 10 / 0 并没有真正被求值.
        //注意:
        //对于 && , 如果左侧表达式值为 false, 则表达式结果一定是 false, 无需计算右侧表达式.
        //对于 ||, 如果左侧表达式值为 true, 则表达式结果一定是 true, 无需计算右侧表达式.
        System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
        System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
    }

    //位运算符
    public static void test6() {
        int a = 10;
        int b = 20;
//        按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0
        System.out.println(a & b);
        //按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1
        System.out.println(a | b);
        boolean c = false;
        boolean d = true;
        System.out.println(c | d);
        System.out.println(c & d);

        //注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为boolean的时候, 表示逻辑运算

        //按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0,取反的所有位
        int e = 0xf;
        System.out.printf("%x\n", ~e);

        //按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1
        int f = 0x1;
        int g = 0x2;
        System.out.printf("%x\n", f ^ g);
    }

    //移位运算
    //移位运算符有三个: << >> >>> ，都是二元运算符，且都是按照二进制比特位来运算的
    public static void test7() {
        //左移 <<: 最左侧位不要了, 最右侧补 0
        //注意：向左移位时，丢弃的是符号位，因此正数左移可能会变成负数
        int a = -1073741827; //1100 0000 0000 0000 0000 0000 0000 0011  原码
                             //1011 1111 1111 1111 1111 1111 1111 1100  反码
                             //1011 1111 1111 1111 1111 1111 1111 1101  补码

                             //0111 1111 1111 1111 1111 1111 1111 1010  原码/反码/补码
        System.out.printf("%d\n", a << 1);

        //右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
        int b = 255;                         //0000 0000 0000 0000 0000 0000 1111 1111原码/反码/补码
        System.out.printf("%d\n", b >> 1);   //0000 0000 0000 0000 0000 0000 01111 111原码/反码/补码

        //1000 0000 0000 0000 0000 0000 1111 1111 补码
        //1000 0000 0000 0000 0000 0000 1111 1110 反码
        //1111 1111 1111 1111 1111 1111 0000 0001 原码
        int c = -2147483393;
        System.out.printf("%d\n", c >> 1); //1100 0000 0000 0000 0000 0000 0111 1111 补码
                                           //1100 0000 0000 0000 0000 0000 0111 1110 反码
                                           //1011 1111 1111 1111 1111 1111 1000 0001 原码

        //无符号右移 >>>: 最右侧位不要了, 最左侧补0
        //1000 0000 0000 0000 0000 0000 1111 1111 补码
        //1000 0000 0000 0000 0000 0000 1111 1110 反码
        //1111 1111 1111 1111 1111 1111 0000 0001 原码
        int d = -2147483393;
        System.out.printf("%d\n", d >>> 1); //0100 0000 0000 0000 0000 0000 0111 1111 原码/反码/补码

        //注意:
        //1. 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方.
        //2. 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方.
        //3. 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替
        //4. 移动负数位或者移位位数过大都没有意义
    }

    //条件运算符
    //条件运算符只有一个:
    //表达式1 ? 表达式2 : 表达式3
    //当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值;
    //当 表达式1 的值为 false 时, 整个表达式的值为 表达式3 的值
    //也是 Java 中唯一的一个 三目运算符, 是条件判断语句的简化写法
    public static void test8() {
        // 求两个整数的最大值
        int a = 10;
        int b = 20;
        int max = a > b ? a : b;
        System.out.println(max);

        //表达式2和表达式3的结果要是同类型的，除非能发生类型隐式类型转换
        int c = 10;
        int d = 20;
//        int e = c > d ? 1 : 2.0;

        //表达式不能单独存在，其产生的结果必须要被使用
//        c > d ? 10 : 20;
    }

    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();
//        test5();
//        test6();
//        test7();
        test8();
    }
}
